package br.cin.ufpe.corba;

import java.lang.reflect.Method;
import java.util.HashMap;

import br.cin.ufpe.corba.iiop.Reply;
import br.cin.ufpe.corba.iiop.Request;
import br.cin.ufpe.corba.interfacerepository.InterfaceDef;

public abstract class Servant extends ObjectRef {
	
	private static HashMap<String, Class<?>> primitiveClasses;
	
	static {
		primitiveClasses = new HashMap<String, Class<?>>();
		primitiveClasses.put(int.class.getCanonicalName(), int.class);
		primitiveClasses.put(long.class.getCanonicalName(), long.class);
		primitiveClasses.put(boolean.class.getCanonicalName(), boolean.class);
		primitiveClasses.put(double.class.getCanonicalName(), double.class);
		primitiveClasses.put(float.class.getCanonicalName(), float.class);
		primitiveClasses.put(byte.class.getCanonicalName(), byte.class);
	}

	public Servant(ORB orb) {
		super(orb);
		set_interface(getInterfaceDef());
		orb.add(this);
	}
	
	public abstract InterfaceDef getInterfaceDef();
		
	public Reply processRequest(Request request) {
		String[] argumentTypes = request.getHeader().getTypes();
		Object[] arguments = request.getBody().getArguments();
		Class<?>[] argumentClasses = new Class<?>[argumentTypes.length];
		
		for (int i = 0; i < arguments.length; i++) {
			if (arguments[i] instanceof ObjectImpl) {				
				arguments[i] = orb.getObject(((ObjectImpl)arguments[i]).objectRef.objectAddress.getKey());
			} else if (arguments[i] instanceof Servant) {
				arguments[i] = orb.getObject(((Servant)arguments[i]).getObjectAddress().getKey());
			}
		}
		
		Reply reply = null;
		try {				
			for(int i = 0; i < arguments.length; i++) {
				if (primitiveClasses.containsKey(argumentTypes[i])){
					argumentClasses[i] = primitiveClasses.get(argumentTypes[i]);
				} else {					
					argumentClasses[i] = Class.forName(argumentTypes[i]);
				}
			}
			
			Method method = null;
					
			method = getClass().getMethod(request.getHeader().getOperation(), argumentClasses);
			
			// Invokes the method
			Object result = method.invoke(this, arguments);
						
			reply = new Reply(request, result);
		} catch (Exception e) {
			if (e.getCause() instanceof SystemException) {
				reply = new Reply(request, (SystemException)e.getCause(), true);
			} else {			
				MethodNotFound result = new MethodNotFound(request.getHeader().getObjectKey(), request.getHeader().getOperation(), request.getBody().getArguments());
				reply = new Reply(request, result, true);
			}
		}
		
		return reply;
	}
	
	@Override
	public boolean non_existent() {
		return false;
	}
}
